package FinalProject;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.PrintWriter;


public class OmokBoard extends Canvas{ /**
	 * 
	 */
	private static final long serialVersionUID = 1L;

// game panel

	public static final int BLACK = 1, WHITE = -1; // white or black

	private int[][] map; // array of omok panel

	private int size; // size of omok game.

	private int cell; // length(pixel)

	private String info = "Stop The Game"; // show the status of game

	private int color = BLACK; // user's color of stone

	// true that is user can play,

	// false that is user cannot play

	private int mode = 0; // 1:single, 2:network
	public static final int SINGLE = 1, NETWORK = 2; // mode
	
	public int getMode() {
		return mode;
	}

	public void setMode(int mode) {
		this.mode = mode;
	}

	private boolean enable = false;

	private boolean running = false; // shows current game is running or not

	private PrintWriter writer; // stream of send message to opponent

	private Graphics gbuff; // graphics object for canvas and buffer

	private Image buff; // buffer for double buffering

	OmokBoard(int s, int c) { // constructor of omok panel(s=number of grid, c=length of each grid)

		this.size = s;
		this.cell = c;

		map = new int[size + 2][]; // decide size of map  .

		for (int i = 0; i < map.length; i++) {
			map[i] = new int[size + 2];
		}

		setBackground(Color.orange); // background color of omok panel.

		setSize(size * (cell + 1) + size - 10, size * (cell + 1) + size - 20); // calculate size of omok panel

		// mouse event
		addMouseListener(new MouseAdapter() {

			public void mousePressed(MouseEvent me) { // click the mouse

				if (!enable)
					return; // if user status is cannot play, escape the action.

				// change position of mouse -> map position.
				int x = (int) Math.round(me.getX() / (double) cell);
				int y = (int) Math.round(me.getY() / (double) cell);

				// if the position is not appropriate position, escape the action 
				if (x == 0 || y == 0 || x == size + 1 || y == size + 1)
					return;

				// if the position has already stone, escape the action
				if (map[x][y] == BLACK || map[x][y] == WHITE)
					return;

				switch (mode) {
				case SINGLE:
					map[x][y] = color;

					// check the result
					if (check(new Point(x, y), color)) {
						if(color == BLACK){
							info = "Black Stone Win.";
						}else{
							info = "White Stone Win.";
						}
						enable = false;	
					}else{
						if(color == BLACK){
							info = "White's turn.";
						}else{
							info = "Black's turn.";
						}
					}
					repaint(); // paint the omok panel
					if(color == BLACK){
						color = WHITE;
					}else{
						color = BLACK;
					}
									
					break;

				case NETWORK:
					// send to the position to opponent
					writer.println("[STONE]" + x + " " + y);

					map[x][y] = color;

					// check the result
					if (check(new Point(x, y), color)) {

						info = "You Win.";
						writer.println("[WIN]");
					}else{
						info = "Wating for opponent' play.";
					}

					repaint(); // paint the omok panel

					// opponent status change that cannot play

					// if opponent play, false->true then you can play

					enable = false;					
					break;					
				default:
					break;
				}
			}

		});

	}

	public boolean isRunning() { // return status of game

		return running;

	}

	public void startGame(String col) { // Start game.
		running = true;
		
		switch (mode) {
		case SINGLE:
			reset();
			enable = true;
			if (col.equals("BLACK")) { // selected black stone

				color = BLACK;
				info = "Start Game... Play First.";

			}

			else { // selected white stone

				color = WHITE;
				info = "Start Game... Wait, opponent trun";

			}
			break;

		case NETWORK:
			if (col.equals("BLACK")) { // selected black stone

				enable = true;
				color = BLACK;

				info = "Start Game... Play First.";

			}

			else { // selected white stone

				enable = false;
				color = WHITE;

				info = "Start Game... Wait, opponent trun";

			}
			break;			
		default:
			break;
		}
	}

	public void stopGame() { // stop the game

		reset(); // initialize omok panel.
		if(mode==NETWORK)
		{
			writer.println("[STOPGAME]"); // send message to opponent.
		}
		enable = false;
		running = false;

	}

	public void putOpponent(int x, int y) { // display opponent's stone

		map[x][y] = -color;

		info = "Opponent put on the stone. Your turn.";

		repaint();

	}

	public void setEnable(boolean enable) {

		this.enable = enable;

	}

	public void setWriter(PrintWriter writer) {

		this.writer = writer;

	}

	public void update(Graphics g) { // when call repaint, automatically called.

		paint(g); // call paint.

	}

	public void paint(Graphics g) { // draw the screen.

		if (gbuff == null) { // if buffer is null, create buffer.

			buff = createImage(getWidth(), getHeight());
			gbuff = buff.getGraphics();
		}

		drawBoard(g); // draw omok panel.

	}

	public void reset() { // initialize omok panel.

		for (int i = 0; i < map.length; i++) {
			for (int j = 0; j < map[i].length; j++) {
				map[i][j] = 0;
			}
		}
		info = "Stop the Game";
		repaint();
	}

	private void drawLine() { // draw line on omok panel.

		gbuff.setColor(Color.black);

		for (int i = 1; i <= size; i++) {

			gbuff.drawLine(cell, i * cell, cell * size, i * cell);

			gbuff.drawLine(i * cell, cell, i * cell, cell * size);

		}
		
		// draw small oval
		int mg1 = 1;
		int mg2 = 1;
		for (int p = 1; p < 4; p++){
			mg2 = 1;
			for (int q = 1; q < 4; q++){
				gbuff.fillOval((cell * (3 * mg1 + 1) - 3), cell * (mg2 * 3)
						+ 20, 6, 6);
				mg2 = mg2 +2;
			}
			mg1 = mg1 +2;
		}

	}

	private void drawBlack(int x, int y) { // draw black stone on(x, y)

		Graphics2D gbuff = (Graphics2D) this.gbuff;
		gbuff.setColor(Color.black);
		gbuff.fillOval(x * cell - cell / 2, y * cell - cell / 2, cell, cell);
		gbuff.setColor(Color.white);
		gbuff.drawOval(x * cell - cell / 2, y * cell - cell / 2, cell, cell);

	}

	private void drawWhite(int x, int y) { // draw white stone on(x, y)

		gbuff.setColor(Color.white);
		gbuff.fillOval(x * cell - cell / 2, y * cell - cell / 2, cell, cell);
		gbuff.setColor(Color.black);
		gbuff.drawOval(x * cell - cell / 2, y * cell - cell / 2, cell, cell);

	}

	private void drawStones() { // draw all stone which is on map already

		for (int x = 1; x <= size; x++)

			for (int y = 1; y <= size; y++) {

				if (map[x][y] == BLACK)

					drawBlack(x, y);

				else if (map[x][y] == WHITE)

					drawWhite(x, y);

			}

	}

	synchronized private void drawBoard(Graphics g) { // draw omok panel.

		// draw buffer first, then draw the image of buffer onto omok panel
		gbuff.clearRect(0, 0, getWidth(), getHeight());
		drawLine();
		drawStones();
		gbuff.setColor(Color.red);
		gbuff.drawString(info, 20, 15);
		g.drawImage(buff, 0, 0, this);

	}

	private boolean check(Point p, int col) {

		if (count(p, 1, 0, col) + count(p, -1, 0, col) == 4)
			return true;

		if (count(p, 0, 1, col) + count(p, 0, -1, col) == 4)
			return true;

		if (count(p, -1, -1, col) + count(p, 1, 1, col) == 4)
			return true;

		if (count(p, 1, -1, col) + count(p, -1, 1, col) == 4)
			return true;

		return false;

	}

	private int count(Point p, int dx, int dy, int col) {

		int i = 0;
		for (; map[p.x + (i + 1) * dx][p.y + (i + 1) * dy] == col; i++);
		return i;

	}

}